package drds.plus.executor.cursor.cursor.impl;

import drds.plus.executor.cursor.cursor.IColumnAliasCursor;
import drds.plus.executor.cursor.cursor.ISortingCursor;
import drds.plus.executor.cursor.cursor_metadata.CursorMetaData;
import drds.plus.executor.cursor.cursor_metadata.CursorMetaDataImpl;
import drds.plus.executor.record_codec.record.Record;
import drds.plus.executor.row_values.RowValues;
import drds.plus.executor.row_values.RowValuesWrapper;
import drds.plus.executor.utils.Utils;
import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.configuration.ColumnMetaData;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.column.Column;
import drds.plus.sql_process.abstract_syntax_tree.expression.order_by.OrderBy;
import drds.plus.util.GeneralUtil;
import drds.tools.$;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 用于做别名替换和select操作
 */
public class ColumnAliasCursor extends SortingCursor implements IColumnAliasCursor {
    @Setter
    @Getter
    private final String tableAlias;
    @Setter
    @Getter
    private final List<Item> returnItemList;
    @Setter
    @Getter
    private List<ColumnMetaData> columnMetaDataList;
    @Setter
    @Getter
    private boolean schemaInited = false;
    @Setter
    @Getter
    private CursorMetaData cursorMetaData;
    @Setter
    @Getter
    private List<ColumnMetaData> returnColumnMetaDataList;

    public ColumnAliasCursor(ISortingCursor cursor, List<Item> returnItemList, String tableAlias) throws RuntimeException {
        super(cursor);
        this.tableAlias = tableAlias;
        this.returnItemList = returnItemList;
        // initAliasSchema(returnItemList, tableAlias);
    }

    private void initAliasSchema(List<Item> returnItemList, String tableAlias, CursorMetaData cursorMetaData) throws RuntimeException {
        if (schemaInited) {
            return;
        }
        columnMetaDataList = new ArrayList<ColumnMetaData>(returnItemList.size());
        List<Integer> indexList = new ArrayList<Integer>(columnMetaDataList.size());
        for (Item item : returnItemList) {
            ColumnMetaData columnMetaData = null;
            String tableName = item.getTableName();
            if (tableAlias != null) {
                if (tableName == null || tableAlias.startsWith(tableName)) {
                    // 如果以tableName作为开始，那么认为认为是相同名字，因此也做截断处理，取alias的.之前的数据。
                    // 最好也让优化器协助，不允许在alias里面出现"."
                    tableAlias = Utils.getTableName(tableAlias);
                }
            }
            if ($.isNotNullAndNotEmpty(item.getAlias())) {
                if ($.isNotNullAndEmpty(tableAlias)) {
                    columnMetaData = Utils.getColumnMetaData(item, item.getAlias());
                } else {
                    columnMetaData = Utils.getColumnMetaData(item, tableAlias, item.getAlias());
                }
            } else {
                if ($.isNotNullAndEmpty(tableAlias)) {
                    columnMetaData = Utils.getColumnMetaData(item);
                } else {
                    columnMetaData = Utils.getColumnMetaTable(item, tableAlias);
                }
            }
            Integer index = cursorMetaData.getIndex(item.getTableName(), item.getColumnName(), item.getAlias());

            if (index != null) {
                indexList.add(index);
                columnMetaDataList.add(columnMetaData);
            }
        }
        if ($.isNotNullAndNotEmpty(tableAlias)) {
            // 如果没有就用下层的tableName
            this.cursorMetaData = CursorMetaDataImpl.buildCursorMetaData(columnMetaDataList, indexList, cursorMetaData.getIndexRange());
            List<OrderBy> orderByList = getOrderByList();
            if (orderByList != null) {
                List<OrderBy> newOrderByList = new ArrayList<OrderBy>(orderByList.size());
                for (OrderBy orderBy : orderByList) {
                    Column column = Utils.getIColumn(orderBy.getItem());
                    Column copy = column.copy();
                    copy.setTableName(tableAlias);
                    copy.setAlias(null);
                    OrderBy orderBy1 = ObjectCreateFactory.createOrderBy();
                    orderBy1.setColumn(copy);
                    orderBy1.setAsc(orderBy.getAsc());
                    newOrderByList.add(orderBy1);
                }
                setOrderByList(newOrderByList);
            }
        } else {
            this.cursorMetaData = CursorMetaDataImpl.buildCursorMetaData(columnMetaDataList, indexList, cursorMetaData.getIndexRange());
        }
        schemaInited = true;
    }

    public RowValues next() throws RuntimeException {
        // 不做判断是否需要replace和select，只进行浅封装
        return replaceAliasAndSelect(parentCursorNext());
    }

    public RowValues prev() throws RuntimeException {
        return replaceAliasAndSelect(parentCursorPrev());
    }

    public RowValues current() throws RuntimeException {
        return replaceAliasAndSelect(parentCursorCurrent());
    }

    public RowValues first() throws RuntimeException {
        return replaceAliasAndSelect(parentCursorFirst());
    }

    public RowValues getNextDuplicateValueRowData() throws RuntimeException {
        return replaceAliasAndSelect(parentCursorGetNextDuplicateRowData());
    }

    public RowValues last() throws RuntimeException {
        return replaceAliasAndSelect(parentCursorLast());
    }

    private RowValues replaceAliasAndSelect(RowValues rowData) throws RuntimeException {
        if (rowData == null) {
            return null;
        }
        initAliasSchema(returnItemList, tableAlias, rowData.getParentCursorMetaData());
        RowValues rowDataWrapper = new RowValuesWrapper(cursorMetaData, rowData);
        return rowDataWrapper;
    }

    public Map<Record, DuplicateValueRowDataLinkedList> mgetRecordToDuplicateValueRowDataLinkedListMap(List<Record> keyRecordList, boolean prefixMatch, boolean keyFilterOrValueFilter) throws RuntimeException {
        Map<Record, DuplicateValueRowDataLinkedList> recordToDuplicateValueLinkedListMap = super.mgetRecordToDuplicateValueRowDataLinkedListMap(keyRecordList, prefixMatch, keyFilterOrValueFilter);
        for (DuplicateValueRowDataLinkedList duplicateValueRowDataLinkedList : recordToDuplicateValueLinkedListMap.values()) {
            while (duplicateValueRowDataLinkedList != null) {
                duplicateValueRowDataLinkedList.rowData = replaceAliasAndSelect(duplicateValueRowDataLinkedList.rowData);
                duplicateValueRowDataLinkedList = duplicateValueRowDataLinkedList.next;
            }
        }
        return recordToDuplicateValueLinkedListMap;
    }

    public String toStringWithInden(int inden) {
        String tabTittle = GeneralUtil.getTab(inden);
        String tabContent = GeneralUtil.getTab(inden + 1);
        StringBuilder sb = new StringBuilder();
        GeneralUtil.printlnToStringBuilder(sb, tabTittle + "ColumnAliasCursor ");
        GeneralUtil.printAFieldToStringBuilder(sb, "tableAlias", this.tableAlias, tabContent);
        GeneralUtil.printAFieldToStringBuilder(sb, "groupByColumnMetaDataList", this.columnMetaDataList, tabContent);
        GeneralUtil.printAFieldToStringBuilder(sb, "returnItemList", this.returnItemList, tabContent);
        GeneralUtil.printAFieldToStringBuilder(sb, "cursorMetaData", this.cursorMetaData, tabContent);
        if (this.cursor != null) {
            sb.append(this.cursor.toStringWithInden(inden + 1));
        }

        return sb.toString();
    }

    public List<ColumnMetaData> getColumnMetaDataList() throws RuntimeException {
        if (this.returnColumnMetaDataList != null) {
            return this.returnColumnMetaDataList;
        }
        returnColumnMetaDataList = new ArrayList();
        for (Item<Item> item : returnItemList) {
            String tableName = tableAlias;
            if (tableName == null) {
                tableName = item.getTableName();
            }
            returnColumnMetaDataList.add(new ColumnMetaData(tableName, item.getColumnName(), item.getAlias(), item.getType(), true));
        }
        return returnColumnMetaDataList;
    }
}
